package cn.yshujia.repository;

import cn.yshujia.domain.dto.PageDTO;
import cn.yshujia.domain.entity.Comment;
import cn.yshujia.domain.entity.Like;
import cn.yshujia.domain.vo.CommentVO;
import cn.yshujia.domain.vo.PageVO;
import cn.yshujia.domain.vo.UserVO;
import cn.yshujia.mapper.CommentMapper;
import cn.yshujia.mapper.LikeMapper;
import cn.yshujia.service.impl.UserServiceImpl;
import cn.yshujia.transform.CommentTransform;
import cn.yshujia.utils.PageUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Repository;
import org.springframework.util.ObjectUtils;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * @author YShuJia
 * @create 2024/12/16
 * @description CommentRepository
 */

@Slf4j
@Repository
public class CommentRepository {
	
	@Resource
	RedisTemplate<String, CommentVO> redis;
	
	@Resource
	UserServiceImpl userService;
	
	@Resource
	CommentMapper mapper;
	
	@Resource
	LikeMapper likeMapper;
	
	private static final String VO = "comment:";
	
	private static final String VO_TREE_HOLE = "comment:tree_hole";
	
	private static final Duration VO_DURATION = Duration.ofDays(3L);
	
	
	@Resource (name = "Task")
	Executor executor;
	
	
	public PageVO<CommentVO> getPage(PageDTO dto) {
		String KEY = VO_TREE_HOLE;
		// 分页
		long start = (long) dto.getSize() * (dto.getCurrent() - 1);
		long end = start + dto.getSize() - 1;
		// 获取参数
		Map<String, Object> dtoParam = dto.getParam();
		// sourceId == null 获取 sourceId 评论 否则获取留言中的评论
		LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
		if (dtoParam != null && dtoParam.get("sourceId") != null) {
			Long sourceId = Long.parseLong(dtoParam.get("sourceId").toString());
			KEY = VO + sourceId;
			wrapper.eq(Comment::getSourceId, sourceId)
					.last(" AND id = floor_id ORDER BY create_time DESC LIMIT " + start + "," + dto.getSize());
		} else {
			wrapper.last(" WHERE id = source_id AND id = floor_id ORDER BY create_time DESC LIMIT " + start + "," + dto.getSize());
		}
		
		// 获取缓存
		List<CommentVO> list = redis.opsForList().range(KEY, start, end);
		if (!ObjectUtils.isEmpty(list)) {
			return PageUtils.getPage(dto, list);
		}
		
		list = new ArrayList<>();
		// 从数据库获取数据
		List<Comment> parent = mapper.selectList(wrapper);
		
		for (Comment comment : parent) {
			CommentVO commentVO = CommentTransform.entity2VO(comment);
			// 获取评论子评论
			CompletableFuture<List<CommentVO>> childrenFuture = getChildren(comment.getId());
			//获取评论用户
			CompletableFuture<UserVO> userFuture = getUserVO(comment.getUserId());
			//获取评论父用户
			CompletableFuture<UserVO> parentUserFuture = getUserVO(comment.getParentUserId());
			//等待所有线程结束
			CompletableFuture<Void> result = CompletableFuture.allOf(userFuture, parentUserFuture, childrenFuture);
			UserVO userVO = null;
			UserVO parentUserVO = null;
			List<CommentVO> children = null;
			try {
				result.get(60, TimeUnit.SECONDS);
				userVO = userFuture.get(60, TimeUnit.SECONDS);
				parentUserVO = parentUserFuture.get(60, TimeUnit.SECONDS);
				children = childrenFuture.get(60, TimeUnit.SECONDS);
			} catch (InterruptedException | ExecutionException | TimeoutException e) {
				log.error("评论查询失败 {}", e.getMessage());
			}
			//组装结果
			commentVO.setUser(userVO);
			commentVO.setParentUser(parentUserVO);
			commentVO.setChildren(children);
			list.add(commentVO);
		}
		if (!ObjectUtils.isEmpty(list)) {
			redis.opsForList().rightPushAll(KEY, list);
			redis.expire(KEY, VO_DURATION);
		}
		return PageUtils.getPage(dto, list);
	}
	
	//获取评论用户
	private CompletableFuture<UserVO> getUserVO(Long id) {
		return CompletableFuture.supplyAsync(() -> userService.getUser(id), executor);
	}
	
	private CompletableFuture<List<CommentVO>> getChildren(Long id) {
		return CompletableFuture.supplyAsync(() -> {
			
			List<Comment> list = mapper.selectList(new LambdaQueryWrapper<Comment>()
					.eq(Comment::getFloorId, id)
					.last(" AND id !=" + id + " ORDER BY create_time DESC"));
			List<CommentVO> voList = new ArrayList<>();
			if (ObjectUtils.isEmpty(list)) {
				return voList;
			}
			for (Comment comment : list) {
				CommentVO commentVO = CommentTransform.entity2VO(comment);
				//获取评论用户
				CompletableFuture<UserVO> userFuture = getUserVO(comment.getUserId());
				//获取评论父用户
				CompletableFuture<UserVO> parentUserFuture = getUserVO(comment.getParentUserId());
				//等待所有线程结束
				CompletableFuture<Void> result = CompletableFuture.allOf(userFuture, parentUserFuture);
				//等待所有线程结束
				UserVO userVO;
				UserVO parentUserVO;
				try {
					result.get(60, TimeUnit.SECONDS);
					userVO = userFuture.get(60, TimeUnit.SECONDS);
					parentUserVO = parentUserFuture.get(60, TimeUnit.SECONDS);
				} catch (InterruptedException | ExecutionException | TimeoutException e) {
					log.error("子评论查询失败 {}", e.getMessage());
					return voList;
				}
				//组装结果
				commentVO.setUser(userVO);
				commentVO.setParentUser(parentUserVO);
				voList.add(commentVO);
			}
			return voList;
		}, executor);
	}
	
	public List<CommentVO> createVOList(List<Comment> list) {
		if (ObjectUtils.isEmpty(list)) {
			return new ArrayList<>();
		}
		List<CommentVO> voList = new ArrayList<>(list.size());
		for (Comment comment : list) {
			CommentVO vo = CommentTransform.entity2VO(comment);
			vo.setUser(userService.getUser(comment.getUserId()));
			vo.setParentUser(userService.getUser(comment.getParentUserId()));
			voList.add(vo);
		}
		return voList;
	}
	
	@Async ("Task")
	public void del() {
		Set<String> keys = redis.keys(VO + "*");
		redis.delete(keys);
	}
	
	@Async ("Task")
	public void delTree() {
		Set<String> keys = redis.keys(VO_TREE_HOLE + "*");
		redis.delete(keys);
	}
	
	@Async ("Task")
	public void delBySourceId(Long sourceId) {
		redis.delete(VO + sourceId);
	}
	
	/**
	 * [] * @return void
	 *
	 * @author YShuJia
	 * @description 每天的凌晨 2点 同步一次评论的点赞数量
	 * @create 2024/12/20 14:55
	 */
	@Async ("Task")
	@Scheduled (cron = "0 0 2 * * ?")
	public void update() {
		del();
		List<Comment> comments = mapper.selectList(new LambdaQueryWrapper<Comment>()
				.select(Comment::getId));
		for (Comment comment : comments) {
			Long count = likeMapper.selectCount(new LambdaQueryWrapper<Like>()
					.eq(Like::getSourceId, comment.getId())
					.eq(Like::getStatus, true));
			comment.setLikeCount(Integer.parseInt(count.toString()));
		}
		mapper.updateById(comments);
	}
	
	@PostConstruct
	public void init() {
		update();
	}
}
